/**
 * @param {Array} arr1
 * @param {Array} arr2
 * @description 得到两个数组的交集, 两个数组的元素为数值或字符串
 */
 export const getIntersection = (arr1, arr2) => {
  let len = Math.min(arr1.length, arr2.length);
  let i = -1;
  let res = [];
  while (++i < len) {
    const item = arr2[i];
    if (arr1.indexOf(item) > -1) res.push(item);
  }
  return res;
};

/**
 * @param {Array} arr1
 * @param {Array} arr2
 * @description 得到两个数组的并集, 两个数组的元素为数值或字符串
 */
export const getUnion = (arr1, arr2) => {
  return Array.from(new Set([...arr1, ...arr2]));
};

/**
 * @param {Array} target 目标数组
 * @param {Array} arr 需要查询的数组
 * @description 判断要查询的数组是否至少有一个元素包含在目标数组中
 */
export const hasOneOf = (targetarr, arr) => {
  return targetarr.some(_ => arr.indexOf(_) > -1);
};

/**
 * @param {String|Number} value 要验证的字符串或数值
 * @param {*} validList 用来验证的列表
 */
export function oneOf(value, validList) {
  for (let i = 0; i < validList.length; i++) {
    if (value === validList[i]) {
      return true;
    }
  }
  return false;
}

/**
 * @param {Number} timeStamp 判断时间戳格式是否是毫秒
 * @returns {Boolean}
 */
const isMillisecond = timeStamp => {
  const timeStr = String(timeStamp);
  return timeStr.length > 10;
};

/**
 * @param {Number} timeStamp 传入的时间戳
 * @param {Number} currentTime 当前时间时间戳
 * @returns {Boolean} 传入的时间戳是否早于当前时间戳
 */
const isEarly = (timeStamp, currentTime) => {
  return timeStamp < currentTime;
};

/**
 * @param {Number} num 数值
 * @returns {String} 处理后的字符串
 * @description 如果传入的数值小于10，即位数只有1位，则在前面补充0
 */
const getHandledValue = num => {
  return num < 10 ? "0" + num : num;
};


/**
 * @param {String|Number} timeStamp 时间戳
 * @returns {String} 相对时间字符串
 */
export const getRelativeTime = timeStamp => {
  // 判断当前传入的时间戳是秒格式还是毫秒
  const IS_MILLISECOND = isMillisecond(timeStamp);
  // 如果是毫秒格式则转为秒格式
  if (IS_MILLISECOND) Math.floor((timeStamp /= 1000));
  // 传入的时间戳可以是数值或字符串类型，这里统一转为数值类型
  timeStamp = Number(timeStamp);
  // 获取当前时间时间戳
  const currentTime = Math.floor(Date.parse(new Date()) / 1000);
  // 判断传入时间戳是否早于当前时间戳
  const IS_EARLY = isEarly(timeStamp, currentTime);
  // 获取两个时间戳差值
  let diff = currentTime - timeStamp;
  // 如果IS_EARLY为false则差值取反
  if (!IS_EARLY) diff = -diff;
  let resStr = "";
  const dirStr = IS_EARLY ? "前" : "后";
  // 少于等于59秒
  if (diff <= 59) resStr = diff + "秒" + dirStr;
  // 多于59秒，少于等于59分钟59秒
  else if (diff > 59 && diff <= 3599)
    resStr = Math.floor(diff / 60) + "分钟" + dirStr;
  // 多于59分钟59秒，少于等于23小时59分钟59秒
  else if (diff > 3599 && diff <= 86399)
    resStr = Math.floor(diff / 3600) + "小时" + dirStr;
  // 多于23小时59分钟59秒，少于等于29天59分钟59秒
  else if (diff > 86399 && diff <= 2623859)
    resStr = Math.floor(diff / 86400) + "天" + dirStr;
  // 多于29天59分钟59秒，少于364天23小时59分钟59秒，且传入的时间戳早于当前
  else if (diff > 2623859 && diff <= 31567859 && IS_EARLY)
    resStr = getDate(timeStamp);
  else resStr = getDate(timeStamp, "year");
  return resStr;
};

/**
 * @returns {String} 当前浏览器名称
 */
export const getExplorer = () => {
  const ua = window.navigator.userAgent;
  const isExplorer = exp => {
    return ua.indexOf(exp) > -1;
  };
  if (isExplorer("MSIE")) return "IE";
  else if (isExplorer("Firefox")) return "Firefox";
  else if (isExplorer("Chrome")) return "Chrome";
  else if (isExplorer("Opera")) return "Opera";
  else if (isExplorer("Safari")) return "Safari";
};



/**
 * 判断一个对象是否存在key，如果传入第二个参数key，则是判断这个obj对象是否存在key这个属性
 * 如果没有传入key这个参数，则判断obj对象是否有键值对
 */
export const hasKey = (obj, key) => {
  if (key) return key in obj;
  else {
    let keysArr = Object.keys(obj);
    return keysArr.length;
  }
};

/**
 * @param {*} obj1 对象
 * @param {*} obj2 对象
 * @description 判断两个对象是否相等，这两个对象的值只能是数字或字符串
 */
export const objEqual = (obj1, obj2) => {
  const keysArr1 = Object.keys(obj1);
  const keysArr2 = Object.keys(obj2);
  if (keysArr1.length !== keysArr2.length) return false;
  else if (keysArr1.length === 0 && keysArr2.length === 0) return true;
  /* eslint-disable-next-line */ else
    return !keysArr1.some(key => obj1[key] != obj2[key]);
};

/**
 * @description 返回 n-m之间随机数
 */
export const getRandom = (n, m) => {
  n = Number(n);
  m = Number(m);
  // 确保 m 始终大于 n
  if (n > m) {
    var temp = n;
    n = m;
    m = temp;
  }
  // 下有详细说明
  return Math.floor(Math.random() * (m - n) + n);
};

/**
 * @param {*} num 个数
 * @description 随机生成校验码
 */
export const randomCode = num => {
  let codeStr = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
  let str = "";
  // 验证码有几位就循环几次
  for (var i = 0; i < num; i++) {
    var ran = getRandom(0, 60);
    str += codeStr.charAt(ran);
  }
  return str;
};

//深度合并数据
export function deepMerge(obj1, obj2) {
  for (let key in obj2) {
    // 如果target(也就是obj1[key])存在，且是对象的话再去调用deepMerge，否则就是obj1[key]里面没这个对象，需要与obj2[key]合并
    // 如果obj2[key]没有值或者值不是对象，此时直接替换obj1[key]
    obj1[key] =
      obj1[key] &&
        obj1[key].toString() === "[object Object]" &&
        obj2[key] && obj2[key].toString() === "[object Object]"
        ? deepMerge(obj1[key], obj2[key])
        : (obj1[key] = obj2[key]);
  }
  return obj1;
}
/**
 * @param {*} val 数据, style 样式 , fixed 保留位, loc 中英文
*/
//格式化数值
export function numberFormat(val, style, fixed = 2, loc) {
  if (!val && !loc) return "NaN";
  if (style == "%") {
    return (val * 100).toFixed(fixed) + "%";
  } else if (style == "cut") {
    return val
      .toFixed(fixed)
      .toString()
      .replace(/\B(?=(?:\d{3})+\b)/g, ",");
  } else if (style == "unit") {
    if (loc == "english") {
      if (val < 100) return val.toFixed(2);
      let param,
        k = 1000,
        sizes = ["", "K", "M", "B"];
      let i = Math.floor(Math.log(val) / Math.log(k));
      if (i < 1) i = 1;
      let valArr = (Math.round(val) / Math.pow(k, i)).toString().split(".");
      if (valArr.length > 1) {
        if (valArr[1].length > 2) {
          param = valArr[0] + valArr[1].toFixed(2) + sizes[i];
        } else {
          param = valArr[0] + valArr[1] + sizes[i];
        }
      } else {
        param = valArr[0] + sizes[i];
      }
      return param;
    } else {
      //中文
      if (val < 1000) return val.toFixed(2);
      let param,
        k = 10000,
        sizes = ["", "万", "亿"];
      let i = Math.floor(Math.log(val) / Math.log(k));
      if (i < 1) i = 1;
      let valArr = (Math.round(val) / Math.pow(k, i)).toString().split(".");
      if (valArr.length > 1) {
        if (valArr[1].length > 2) {
          param = valArr[0] + "." + valArr[1].substring(0, 2) + sizes[i];
        } else {
          param = valArr[0] + "." + valArr[1] + sizes[i];
        }
      } else {
        param = valArr[0] + sizes[i];
      }
      return param;
    }
  } else if (style == "fixed") {
    return val.toFixed(fixed);
  } else {
    return val;
  }
}


/**格式化日期
 * date new Date()
 * type d日 m月 y年
 * next 之前 多少天 多少月 多少年
 * format 返回格式
 */

export function formatDate({ date = new Date(), format = "yyyy-MM-dd", type, next }) {
  const opt = {
    "y+": date.getFullYear(), // 年
    "M+": date.getMonth() + 1, // 月
    "d+": date.getDate(), // 日
    "h+": date.getHours(), // 时
    "m+": date.getMinutes(), // 分
    "s+": date.getSeconds() // 秒
  };
  if (type && next) {
    switch (type) {
      case "y":
        opt["y+"] = opt["y+"] - next;
        break;
      case "m":
        opt["y+"] = opt["y+"] - parseInt(next / 12);
        if (next % 12 < opt["M+"]) {
          opt["M+"] = opt["M+"] - (next % 12);
        } else {
          //当大于当前月
          opt["y+"] = opt["y+"] - parseInt(next / 12) - 1;
          opt["M+"] = 12 + opt["M+"] - (next % 12);
        }
        break;
      case "d":
        let timer = 1000 * 60 * 60 * 24 * next;
        let newDate = new Date(date.getTime() - timer);
        opt["y+"] = newDate.getFullYear();
        opt["M+"] = newDate.getMonth() + 1;
        opt["d+"] = newDate.getDate();
        break;
      default:
        break;
    }
  }
  for (var k in opt) {
    if (k == "y+") {
      if (/(y+)/.test(format))
        format = format.replace(
          RegExp.$1,
          (opt[k] + "").substr(4 - RegExp.$1.length)
        );
    } else {
      if (new RegExp("(" + k + ")").test(format)) {
        format = format.replace(
          RegExp.$1,
          RegExp.$1.length == 1
            ? opt[k]
            : ("00" + opt[k]).substr(("" + opt[k]).length)
        );
      }
    }
  }
  return format;
}

/**
 * 防抖
 */
export function debounce(fn, delay) {
  let timeout = null; // 创建一个标记用来存放定时器的返回值
  return function (e) {
    // 每当用户输入的时候把前一个 setTimeout clear 掉
    clearTimeout(timeout);
    // 然后又创建一个新的 setTimeout, 这样就能保证interval 间隔内如果时间持续触发，就不会执行 fn 函数
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  };
}

/**
 * 截流
 */
export function throttle(handler, wait) {
  // handler为函数  wait为时间
  var lastTime = 0;
  return function () {
    var nowTime = new Date().getTime(); //获取时间
    if (nowTime - lastTime > wait) {
      // 判断当前单击和上次单击的时间是否超过规定的时间
      handler();
      lastTime = nowTime; // 执行后将上次时间进行更新
    }
  };
}

/**
 * 判断对象是否相同
 */
export function isObjectValueEqual(newData, oldData) {
  //类型为基本类型时,如果相同,则返回true
  if (oldData === newData) return true;
  if (
    isObject(oldData) &&
    isObject(newData) &&
    Object.keys(oldData).length === Object.keys(newData).length
  ) {
    //类型为对象并且元素个数相同

    //遍历所有对象中所有属性,判断元素是否相同
    for (const key in oldData) {
      if (oldData.hasOwnProperty(key)) {
        if (!isObjectValueEqual(oldData[key], newData[key]))
          //对象中具有不相同属性 返回false
          return false;
      }
    }
  } else if (
    isArray(oldData) &&
    isArray(newData) &&
    oldData.length === newData.length
  ) {
    //类型为数组并且数组长度相同
    for (let i = 0, length = oldData.length; i < length; i++) {
      if (!isObjectValueEqual(oldData[i], newData[i]))
        //如果数组元素中具有不相同元素,返回false
        return false;
    }
  } else {
    //其它类型,均返回false
    return false;
  }

  //走到这里,说明数组或者对象中所有元素都相同,返回true
  return true;
}

//判断是否为对象
function isObject(obj) {
  return Object.prototype.toString.call(obj) === "[object Object]";
}

// 判断是否数组
function isArray(arr) {
  return Object.prototype.toString.call(arr) === "[object Array]";
}

/**
 * json 转 fromData 形式
 */
export function toFormData(params) {
  const formData = new FormData();
  Object.keys(params).forEach((key) => {
    formData.append(key, params[key]);
  });
  return formData;
}


/**
 * 数组扁平化
 * @param {arr, childKey, selKey, onlyChild} 扁平类数组, 子节点的字段, 获取字段的键子对, 是否获取最底层的键值
 */
export function flatten(arr, childKey, selKey, onlyChild = false) {
  if (!Array.isArray(arr)) return arr;
  return arr.reduce((pre, cur) => {
    if (childKey && Array.isArray(cur[childKey]) && cur[childKey].length > 0) {
      if (!onlyChild && selKey) pre.push(cur[selKey]);
      return pre.concat(flatten(cur[childKey], childKey, selKey, onlyChild))
    } else {
      return pre.concat(cur[selKey])
    }
  },[])
}